home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / tde210.zip / BLOCK.C next >
C/C++ Source or Header  |  1992-11-13  |  87KB  |  2,544 lines

  1. /*******************  start of original comments  ********************/
  2. /*
  3.  * Written by Douglas Thomson (1989/1990)
  4.  *
  5.  * This source code is released into the public domain.
  6.  */
  7.  
  8. /*
  9.  * Name:    dte - Doug's Text Editor program - block commands module
  10.  * Purpose: This file contains all the commands than manipulate blocks.
  11.  * File:    block.c
  12.  * Author:  Douglas Thomson
  13.  * System:  this file is intended to be system-independent
  14.  * Date:    October 1, 1989
  15.  */
  16. /*********************  end of original comments   ********************/
  17.  
  18. /*
  19.  * The block routines have been EXTENSIVELY rewritten.  This editor uses LINE,
  20.  * STREAM, and BOX blocks.  That is, one may mark entire lines, streams of
  21.  * characters, or column blocks.  Block operations are done in place.  There
  22.  * are no paste and cut buffers.  In limited memory situations, larger block
  23.  * operations can be carried out.  Block operations can be done within or
  24.  * across files.  One disadvantage of not using buffers is that block
  25.  * operations can be slow.  The most complicated routine in this editor is by
  26.  * far "move_copy_delete_overlay_block( window )".  I put some comments in,
  27.  * but it is still a bitch.  Come to think of it, most of these block functions
  28.  * are a bitch.
  29.  *
  30.  * Maybe in the next version I'll use buffers to speed up block operations.
  31.  *
  32.  * In TDE, version 1.1, I separated the BOX and LINE actions.  LINE actions
  33.  * are a LOT faster, now.  Still need to speed up BOX actions.
  34.  *
  35.  * In TDE, version 1.3, I put STREAM blocks back in.  Added block upper case,
  36.  * block lower case, and block strip high bit.
  37.  *
  38.  * In TDE, version 1.4, I added a block number function.  Here at our lab,
  39.  * I often need to number samples, lines, etc..., comes in fairly useful.
  40.  *
  41.  * In TDE, version 2.0, I added a box block sort function.
  42.  *
  43.  * In TDE, version 2.0e, I added BlockRot13, BlockFixUUE, and BlockEmailReply.
  44.  *
  45.  * New editor name:  TDE, the Thomson-Davis Editor.
  46.  * Author:           Frank Davis
  47.  * Date:             June 5, 1991, version 1.0
  48.  * Date:             July 29, 1991, version 1.1
  49.  * Date:             October 5, 1991, version 1.2
  50.  * Date:             January 20, 1992, version 1.3
  51.  * Date:             February 17, 1992, version 1.4
  52.  * Date:             April 1, 1992, version 1.5
  53.  * Date:             June 5, 1992, version 2.0
  54.  * Date:             October 31, 1992, version 2.1
  55.  *
  56.  * This modification of Douglas Thomson's code is released into the
  57.  * public domain, Frank Davis.  You may distribute it freely.
  58.  */
  59.  
  60. #include "tdestr.h"
  61. #include "common.h"
  62. #include "tdefunc.h"
  63. #include "define.h"
  64.  
  65.  
  66. /*
  67.  * Name:    mark_block
  68.  * Purpose: To record the position of the start of the block in the file.
  69.  * Date:    June 5, 1991
  70.  * Passed:  window:  pointer to current window
  71.  * Notes:   Assume the user will mark begin and end of a block in either
  72.  *           line, stream, or box mode.  If the user mixes types, then block
  73.  *           type defaults to current block type.
  74.  */
  75. int  mark_block( WINDOW *window )
  76. {
  77. int type;
  78. int num;
  79. long lnum;
  80. register file_infos *file;     /* temporary file variable */
  81. register WINDOW *win;  /* put window pointer in a register */
  82. int rc;
  83.  
  84.    win  = window;
  85.    file = win->file_info;
  86.    if (win->rline > file->length)
  87.       return( ERROR );
  88.    if (g_status.marked == FALSE) {
  89.       g_status.marked = TRUE;
  90.       g_status.marked_file = file;
  91.    }
  92.    if (g_status.command == MarkBox)
  93.       type = BOX;
  94.    else if (g_status.command == MarkLine)
  95.       type = LINE;
  96.    else if (g_status.command == MarkStream)
  97.       type = STREAM;
  98.  
  99.    rc = OK;
  100.    /*
  101.     * define blocks for only one file.  it is ok to modify blocks in any window
  102.     * pointing to original marked file.
  103.     */
  104.    if (file == g_status.marked_file) {
  105.  
  106.       /*
  107.        * mark beginning and ending column regardless of block mode.
  108.        */
  109.       if (file->block_type == NOTMARKED) {
  110.          file->block_ec  = file->block_bc = win->rcol;
  111.          file->block_er  = file->block_br = win->rline;
  112.       } else {
  113.          if (file->block_br > win->rline) {
  114.             file->block_br = win->rline;
  115.             if (file->block_bc < win->rcol && type != STREAM)
  116.                file->block_ec = win->rcol;
  117.             else
  118.                file->block_bc = win->rcol;
  119.          } else {
  120.             if (type != STREAM) {
  121.                file->block_ec = win->rcol;
  122.                file->block_er = win->rline;
  123.             } else {
  124.                if (win->rline == file->block_br &&
  125.                    win->rline == file->block_er) {
  126.                   if (win->rcol < file->block_bc)
  127.                      file->block_bc = win->rcol;
  128.                   else
  129.                      file->block_ec = win->rcol;
  130.                } else if (win->rline == file->block_br)
  131.                   file->block_bc = win->rcol;
  132.                else {
  133.                   file->block_ec = win->rcol;
  134.                   file->block_er = win->rline;
  135.                }
  136.             }
  137.          }
  138.  
  139.          /*
  140.           * if user marks ending line less than beginning line then switch
  141.           */
  142.          if (file->block_er < file->block_br) {
  143.             lnum = file->block_er;
  144.             file->block_er = file->block_br;
  145.             file->block_br = lnum;
  146.          }
  147.  
  148.          /*
  149.           * if user marks ending column less than beginning column then switch
  150.           */
  151.          if ((file->block_ec < file->block_bc) && (type != STREAM ||
  152.               (type == STREAM && file->block_br == file->block_er))) {
  153.             num = file->block_ec;
  154.             file->block_ec = file->block_bc;
  155.             file->block_bc = num;
  156.          }
  157.       }
  158.  
  159.       /*
  160.        * block type in now defined.  if user mixes block types then block
  161.        * is defined as current block type.
  162.        */
  163.       if (file->block_type != NOTMARKED) {
  164.          /*
  165.           * if block type goes to BOX, check to make sure ec is greater than
  166.           * or equal to bc.  ec can be less than bc in STREAM blocks.
  167.           */
  168.          if (type == BOX) {
  169.             if (file->block_ec < file->block_bc) {
  170.                num = file->block_ec;
  171.                file->block_ec = file->block_bc;
  172.                file->block_bc = num;
  173.             }
  174.          }
  175.       }
  176.       file->block_type = type;
  177.       file->dirty = GLOBAL;
  178.    } else {
  179.       /*
  180.        * block already defined
  181.        */
  182.       error( WARNING, win->bottom_line, block1 );
  183.       rc = ERROR;
  184.    }
  185.    return( rc );
  186. }
  187.  
  188.  
  189. /*
  190.  * Name:    unmark_block
  191.  * Purpose: To set all block information to NULL or 0
  192.  * Date:    June 5, 1991
  193.  * Passed:  arg_filler: variable to match array of function pointers prototype
  194.  * Notes:   Reset all block variables if marked, otherwise return.
  195.  *           If a block is unmarked then redraw the screen(s).
  196.  */
  197. int  unmark_block( WINDOW *arg_filler )
  198. {
  199. register file_infos *marked_file;
  200.  
  201.    if (g_status.marked == TRUE) {
  202.       marked_file              = g_status.marked_file;
  203.       g_status.marked          = FALSE;
  204.       g_status.marked_file     = NULL;
  205.       marked_file->block_start = NULL;
  206.       marked_file->block_end   = NULL;
  207.       marked_file->block_bc    = marked_file->block_ec = 0;
  208.       marked_file->block_br    = marked_file->block_er = 0l;
  209.       if (marked_file->block_type)
  210.          marked_file->dirty = GLOBAL;
  211.       marked_file->block_type  = NOTMARKED;
  212.    }
  213.    return( OK );
  214. }
  215.  
  216.  
  217. /*
  218.  * Name:    restore_marked_block
  219.  * Purpose: To restore block beginning and ending row after an editing function
  220.  * Date:    June 5, 1991
  221.  * Passed:  window:  pointer to current window
  222.  *          net_change: number of bytes added or subtracted
  223.  * Notes:   If a change has been made before the marked block then the
  224.  *           beginning and ending row need to be adjusted by the number of
  225.  *           lines added or subtracted from file.
  226.  */
  227. void restore_marked_block( WINDOW *window, int net_change )
  228. {
  229. long length;
  230. register file_infos *marked_file;
  231.  
  232.    if (g_status.marked == TRUE && net_change != 0) {
  233.       marked_file = g_status.marked_file;
  234.       length = marked_file->length;
  235.  
  236.       /*
  237.        * restore is needed only if a block is defined and window->file_info is
  238.        * same as marked file and there was a net change in file length.
  239.        */
  240.       if (marked_file == window->file_info) {
  241.  
  242.          /*
  243.           * if cursor is before marked block then adjust block by net change.
  244.           */
  245.          if (marked_file->block_br > window->rline) {
  246.             marked_file->block_br += net_change;
  247.             marked_file->block_er += net_change;
  248.             marked_file->dirty = GLOBAL;
  249.          /*
  250.           * if cursor is somewhere in marked block don't restore, do redisplay
  251.           */
  252.          } else if (marked_file->block_er >= window->rline)
  253.             marked_file->dirty = GLOBAL;
  254.  
  255.          /*
  256.           * check for lines of marked block beyond end of file
  257.           */
  258.          if (marked_file->block_br > length)
  259.             unmark_block( window );
  260.          else if (marked_file->block_er > length) {
  261.             marked_file->block_er = length;
  262.             marked_file->dirty = GLOBAL;
  263.          }
  264.       }
  265.    }
  266. }
  267.  
  268.  
  269. /*
  270.  * Name:    prepare_block
  271.  * Purpose: To prepare a window/file for a block read, move or copy.
  272.  * Date:    June 5, 1991
  273.  * Passed:  window:  pointer to current window
  274.  *          file: pointer to file information.
  275.  *          text_line: pointer to line in file to prepare.
  276.  *          lend: line length.
  277.  *          bc: beginning column of BOX.
  278.  * Notes:   The main complication is that the cursor may be beyond the end
  279.  *              of the current line, in which case extra padding spaces have
  280.  *              to be added before the block operation can take place.
  281.  *              this only occurs in BOX and STREAM operations.
  282.  *          since we are padding a line, do not trim trailing space.
  283.  */
  284. int  prepare_block( WINDOW *window, text_ptr text_line, int lend, int bc )
  285. {
  286. register int pad = 0;   /* amount of padding to be added */
  287. register char *source;  /* source for block moves */
  288. int detab_len;
  289. int detab_add;
  290.  
  291.    copy_line( text_line, window->bottom_line );
  292.  
  293.    if (mode.inflate_tabs) {
  294.       /*
  295.        * work out how much padding is required to extend the current
  296.        *  line to the cursor position after tabs are expanded.
  297.        */
  298.       detab_linebuff( );
  299.       detab_len = linelen( g_status.line_buff );
  300.       detab_add = detab_len - lend;
  301.  
  302.       if (detab_len < bc) {
  303.          pad = bc - detab_len;
  304.          lend = detab_len;
  305.       }
  306.    } else {
  307.       /*
  308.        * work out how much padding is required to extend the current
  309.        *  line to the cursor position
  310.        */
  311.       detab_add = 0;
  312.       pad = bc - lend;
  313.    }
  314.  
  315.  
  316.    if (pad > 0  || detab_add > 0) {
  317.       if (pad > 0) {
  318.          /*
  319.           * make room for the padding spaces
  320.           */
  321.          source = g_status.line_buff + lend;
  322.          memmove( source+pad, source, pad+2 );
  323.  
  324.          /*
  325.           * insert the padding spaces
  326.           */
  327.          memset( source, ' ', pad );
  328.       }
  329.  
  330.       /*
  331.        * if mode.inflate_tabs, let's don't entab the line until we get
  332.        *   thru processing this line, e.g. copying, numbering....
  333.        */
  334.       un_copy_line( text_line, window, FALSE );
  335.    }
  336.  
  337.    g_status.copied = FALSE;
  338.  
  339.    return( pad + detab_add );
  340. }
  341.  
  342.  
  343. /*
  344.  * Name:    pad_dest_line
  345.  * Purpose: To prepare a window/file for a block move or copy.
  346.  * Date:    June 5, 1991
  347.  * Passed:  window:  pointer to current window
  348.  *          dest_file: pointer to file information.
  349.  *          dest_line: pointer to line in file to prepare.
  350.  * Notes:   We are doing a BOX action (except DELETE).   We have come
  351.  *          to the end of the file and have no more lines.  All this
  352.  *          routine does is add a blank line to file.
  353.  */
  354. void pad_dest_line( WINDOW *window, file_infos *dest_file, text_ptr dest_line)
  355. {
  356.    /*
  357.     * put linefeed in line_buff. dest_line should be pointing to
  358.     * file->end_text - 1.  since we inserted line feed, increment file length.
  359.     */
  360.    g_status.line_buff[0] = '\n';
  361.    g_status.line_buff[1] = CONTROL_Z;
  362.    g_status.copied = TRUE;
  363.    un_copy_line( dest_line, window, FALSE );
  364.    ++dest_file->length;
  365. }
  366.  
  367.  
  368. /*
  369.  * Name:    move_copy_delete_overlay_block
  370.  * Purpose: Master BOX, STREAM, or LINE routine.
  371.  * Date:    June 5, 1991
  372.  * Passed:  window:  pointer to current window
  373.  * Notes:   Operations on BOXs, STREAMs, or LINEs require several common
  374.  *           operations.  All require finding the beginning and ending marks.
  375.  *           The big differences are whether to delete the source block, copy
  376.  *           the source block, or leave the source block marked.
  377.  *          This routine will handle block operations across files.  Since one
  378.  *           must determine the relationship of source and destination blocks
  379.  *           within a file, it is relatively easy to expand this relationship
  380.  *           across files.  There are several caveats.  Most deal with the
  381.  *           difference between LINE and BOX operations others deal with
  382.  *           differences between operations within a file and operations
  383.  *           across files.
  384.  *          This is probably the most complicated routine in the editor.  It
  385.  *           is not easy to understand.
  386.  */
  387. int  move_copy_delete_overlay_block( WINDOW *window )
  388. {
  389. int action;
  390. WINDOW *source_window; /* source window for block moves */
  391. text_ptr source;        /* source for block moves */
  392. text_ptr dest;          /* destination for block moves */
  393. text_ptr p;             /* temporary text pointer */
  394. long number;            /* number of characters for block moves */
  395. int lens;               /* length of source line */
  396. int lend;               /* length of destination line */
  397. int add;                /* characters being added from another line */
  398. int block_len;          /* length of the block */
  399. text_ptr block_start;   /* start of block in file */
  400. text_ptr block_end;     /* end of block in file - not same for LINE or BOX */
  401. char block_buff[BUFF_SIZE+2];
  402. char swap_buff[BUFF_SIZE+2];
  403. int prompt_line;
  404. int same;               /* are these files the same */
  405. int source_first;       /* is source file lower in memory than dest */
  406. file_infos *source_file, *dest_file;
  407. int rcol, bc, ec;       /* temporary column variables */
  408. int xbc, xec;           /* temporary column variables */
  409. long rline;             /* temporary real line variable */
  410. long br, er, li;        /* temporary line variables */
  411. long dest_add;          /* number of bytes added to destination file */
  412. long source_sub;        /* number of bytes sub from source file */
  413. long diff;
  414. long block_num;         /* starting number for block number */
  415. long block_inc;         /* increment to use for block number */
  416. int  block_just;        /* left or right justify numbers? */
  417. unsigned long block_size;
  418. int block_type;
  419. int fill_char;
  420. WINDOW s_w, d_w;       /* a couple of temporary WINDOWs for BOX stuff */
  421. int  padded_file;
  422. WINDOW *w;
  423.  
  424.    /*
  425.     * initialize block variables
  426.     */
  427.    entab_linebuff( );
  428.    un_copy_line( window->cursor, window, TRUE );
  429.    if (g_status.marked == FALSE)
  430.       return( ERROR );
  431.    switch (g_status.command) {
  432.       case MoveBlock :
  433.          action = MOVE;
  434.          break;
  435.       case DeleteBlock :
  436.          action = DELETE;
  437.          break;
  438.       case CopyBlock :
  439.          action = COPY;
  440.          break;
  441.       case KopyBlock :
  442.          action = KOPY;
  443.          break;
  444.       case FillBlock :
  445.          action = FILL;
  446.          break;
  447.       case OverlayBlock :
  448.          action = OVERLAY;
  449.          break;
  450.       case NumberBlock :
  451.          action = NUMBER;
  452.          break;
  453.       case SwapBlock :
  454.          action = SWAP;
  455.          break;
  456.    }
  457.    source_file = g_status.marked_file;
  458.    source_window = g_status.window_list;
  459.    for (; ptoul( source_window->file_info ) != ptoul( source_file );)
  460.       source_window = source_window->next;
  461.    prompt_line = window->bottom_line;
  462.    dest_file = window->file_info;
  463.    check_block( );
  464.    if (g_status.marked == FALSE)
  465.       return( ERROR );
  466.    block_start = source_file->block_start;
  467.    block_end = source_file->block_end;
  468.    block_type = source_file->block_type;
  469.    dest = window->cursor = cpf( window->cursor );
  470.    rline = window->rline;
  471.  
  472.    /*
  473.     * set up Beginning Column, Ending Column, Beginning Row, Ending Row
  474.     */
  475.    bc = source_file->block_bc;
  476.    ec = source_file->block_ec;
  477.    br = source_file->block_br;
  478.    er = source_file->block_er;
  479.  
  480.    /*
  481.     * if we are BOX FILLing or BOX NUMBERing, beginning column is bc,
  482.     *   not the column of cursor
  483.     */
  484.    rcol =  (action == FILL || action == NUMBER) ? bc : window->rcol;
  485.    dest_add = source_sub = 0;
  486.  
  487.    /*
  488.     * if this is a LINE action, put the text below the current line
  489.     */
  490.    if (block_type == LINE && action != DELETE)
  491.       if ((p = find_next( dest )) != NULL)
  492.          dest = p;
  493.    /*
  494.     * must find out if source and destination file are the same.
  495.     * it don't matter with FILL and DELETE - those actions only modify the
  496.     * source file.
  497.     */
  498.    same = FALSE;
  499.    if (action == FILL) {
  500.       if (block_type == BOX) {
  501.          if (get_block_fill_char( window, &fill_char ) == ERROR)
  502.             return( ERROR );
  503.          dest = block_start;
  504.          same = TRUE;
  505.       } else {
  506.          /*
  507.           * can only fill box blocks.
  508.           */
  509.          error( WARNING, prompt_line, block2 );
  510.          return( ERROR );
  511.       }
  512.    }
  513.    if (action == NUMBER) {
  514.       if (block_type == BOX) {
  515.          if (get_block_numbers( window, &block_num, &block_inc, &block_just )
  516.               == ERROR)
  517.             return( ERROR );
  518.          dest = block_start;
  519.          same = TRUE;
  520.       } else {
  521.          /*
  522.           * can only number box blocks.
  523.           */
  524.          error( WARNING, prompt_line, block3a );
  525.          return( ERROR );
  526.       }
  527.    }
  528.    if (action == SWAP) {
  529.       if (block_type != BOX) {
  530.          /*
  531.           * can only swap box blocks.
  532.           */
  533.          error( WARNING, prompt_line, block3b );
  534.          return( ERROR );
  535.       }
  536.    }
  537.    if (source_file == dest_file && action != DELETE && action != FILL) {
  538.       same = TRUE;
  539.       if (block_type == BOX && action == MOVE) {
  540.          if (rline == br  &&  (rcol >= bc && rcol <= ec))
  541.              /*
  542.               * a block moved to within the block itself has no effect
  543.               */
  544.             return( ERROR );
  545.       } else if (block_type == LINE || block_type == STREAM) {
  546.          if (rline >= br && rline <= er) {
  547.             if (block_type == LINE) {
  548.                 /*
  549.                  * if COPYing or KOPYing within the block itself, reposition the
  550.                  * destination to the next line after the block (if it exists)
  551.                  */
  552.                if (action == COPY || action == KOPY)
  553.                   dest = cpf( block_end );
  554.                 /*
  555.                  * a block moved to within the block itself has no effect
  556.                  */
  557.                else if (action == MOVE)
  558.                   return( ERROR );
  559.             } else {
  560.  
  561.                /*
  562.                 * to find out if cursor is in a STREAM block we have to do
  563.                 * a few more tests.  if cursor is on the beginning row or
  564.                 * ending row, then check the beginning and ending column.
  565.                 */
  566.                if ((rline > br && rline < er) || (rline == br && rcol >= bc) ||
  567.                    (rline == er && rcol <= ec)) {
  568.  
  569.                   /*
  570.                    * if the cursor is in middle of STREAM, make destination
  571.                    * the last character following the STREAM block.
  572.                    */
  573.                   if (action == COPY || action == KOPY) {
  574.                      dest = cpf( block_end );
  575.                      rcol = ec + 1;
  576.                      rline = er;
  577.                   } else if (action == MOVE)
  578.                      return( ERROR );
  579.                }
  580.             }
  581.          }
  582.       }
  583.    }
  584.  
  585.    /*
  586.     * must know if source of block is before or after destination
  587.     */
  588.    source_first = FALSE;
  589.    if (ptoul( dest ) > ptoul( source_file->block_start ))
  590.       source_first = TRUE;
  591.    if (same && block_type == BOX) {
  592.       if ( rline >= br)
  593.          source_first = TRUE;
  594.    }
  595.  
  596.    /*
  597.     * work out how much has to be moved
  598.     */
  599.    if (block_type == BOX) {
  600.       block_size = ((ec+1) - bc) * ((er+1) - br);
  601.       if (action != DELETE)
  602.          block_size += ((rcol+1) * ((er+1) - br));
  603.       else
  604.          block_size = 0;
  605.    } else if (block_type == LINE || block_type == STREAM) {
  606.       if (action != DELETE)
  607.          block_size = ptoul( block_end ) - ptoul( block_start );
  608.       else
  609.          block_size = 0;
  610.    } else
  611.       return( ERROR );
  612.  
  613.    /*
  614.     * check that there is room to add block to file
  615.     */
  616.    if (ptoul( g_status.end_mem ) + block_size >= ptoul( g_status.max_mem )) {
  617.       /*
  618.        * not enough memory for block.
  619.        */
  620.       error( WARNING, prompt_line, block4 );
  621.       return( ERROR );
  622.    }
  623.  
  624.    /*
  625.     * set the command to word wrap so the un_copy_line function will
  626.     * not display the lines while doing block stuff.
  627.     */
  628.    g_status.command = WordWrap;
  629.  
  630.    /*
  631.     * 1. can't create lines greater than g_display.line_length
  632.     * 2. if we are FILLing a BOX - fill block buff once right here
  633.     * 3. only allow overlaying BOXs
  634.     */
  635.    if (block_type == BOX) {
  636.       block_len = (ec+1) - bc;
  637.       if (action != DELETE && action != FILL) {
  638.          if (rcol + block_len > MAX_LINE_LENGTH) {
  639.             /*
  640.              * line too long
  641.              */
  642.             error( WARNING, prompt_line, ltol );
  643.             return( ERROR );
  644.          }
  645.       } else if (action == FILL)
  646.          block_fill( block_buff, fill_char, block_len );
  647.    } else if (block_type == LINE) {
  648.       block_len = 0;
  649.       if (action == OVERLAY) {
  650.          /*
  651.           * can only overlay box blocks
  652.           */
  653.          error( WARNING, prompt_line, block5 );
  654.          return( ERROR );
  655.       }
  656.    } else if (block_type == STREAM) {
  657.  
  658.       if (action == OVERLAY) {
  659.          /*
  660.           * can only overlay box blocks
  661.           */
  662.          error( WARNING, prompt_line, block5 );
  663.          return( ERROR );
  664.       }
  665.  
  666.       lend = linelen( block_end );
  667.       if (action == DELETE || action == MOVE) {
  668.  
  669.          /*
  670.           * Is what's left on start of STREAM block line plus what's left at
  671.           * end of STREAM block line too long?
  672.           */
  673.          if (lend > ec)
  674.             lend -= ec;
  675.          else
  676.             lend = 0;
  677.          if (bc + lend > MAX_LINE_LENGTH) {
  678.             /*
  679.              * line too long
  680.              */
  681.             error( WARNING, prompt_line, ltol );
  682.             return( ERROR );
  683.          }
  684.       }
  685.  
  686.       if (action != DELETE) {
  687.  
  688.          /*
  689.           * We are doing a MOVE, COPY, or KOPY.  Find out if what's on the
  690.           * current line plus the start of the STREAM line are too long.
  691.           * Then find out if end of the STREAM line plus what's left of
  692.           * the current line are too long.
  693.           */
  694.          lens = linelen( block_start );
  695.  
  696.          /*
  697.           * if we had to move the destination of the STREAM COPY or KOPY
  698.           * to the end of the STREAM block, then dest and window->cursor
  699.           * will not be the same.  In this case, set length to length of
  700.           * first line in STREAM block.  Then we can add the residue of
  701.           * the first line in block plus residue of the last line of block.
  702.           */
  703.          if (ptoul( dest ) == ptoul( window->cursor ))
  704.             add = linelen( dest );
  705.          else
  706.             add = lens;
  707.  
  708.          /*
  709.           * Is current line plus start of STREAM block line too long?
  710.           */
  711.          if (lens > bc)
  712.             lens -= bc;
  713.          else
  714.             lens = 0;
  715.          if (rcol + lens > MAX_LINE_LENGTH) {
  716.             /*
  717.              * line too long
  718.              */
  719.             error( WARNING, prompt_line, ltol );
  720.             return( ERROR );
  721.          }
  722.  
  723.          /*
  724.           * Is residue of current line plus residue of STREAM block line
  725.           * too long?
  726.           */
  727.          if (add > bc)
  728.             add -= bc;
  729.          else
  730.             add = 0;
  731.          if (lend > ec)
  732.             lend -= ec;
  733.          else
  734.             lend = 0;
  735.          if (add + lend > MAX_LINE_LENGTH) {
  736.             /*
  737.              * line too long
  738.              */
  739.             error( WARNING, prompt_line, ltol );
  740.             return( ERROR );
  741.          }
  742.       }
  743.    }
  744.  
  745.    /*
  746.     * all block actions go forward thru file - check those pointers
  747.     */
  748.    source = cpf( block_start );
  749.    dest = cpf( dest );
  750.  
  751.    /*
  752.     *  -------  main loop for LINE and STREAM blocks  -------------
  753.     */
  754.    if (block_type == LINE || block_type == STREAM) {
  755.       if (block_type == STREAM) {
  756.          dup_window_info( &s_w, source_window );
  757.          dup_window_info( &d_w, window );
  758.          s_w.rline = br;
  759.          d_w.rline = rline;
  760.  
  761.          /*
  762.           * pad the start of the STREAM block if needed.
  763.           */
  764.          lens = linelen( block_start );
  765.          if (lens < bc || mode.inflate_tabs) {
  766.             add = prepare_block( &s_w, block_start, lens, bc );
  767.             if (br != er)
  768.                block_end += add;
  769.             if (source_first)
  770.                dest += add;
  771.          }
  772.          block_start += bc;
  773.          source = cpf( block_start );
  774.  
  775.          /*
  776.           * pad the end of the STREAM block if needed.
  777.           */
  778.          lens = linelen( block_end );
  779.          if (lens < ec+1  || mode.inflate_tabs) {
  780.             add = prepare_block( &s_w, block_end, lens, ec+1 );
  781.             if (source_first)
  782.                dest += add;
  783.          }
  784.          block_end += ec + 1;
  785.  
  786.          /*
  787.           * pad the destination line if necessary
  788.           */
  789.          lend = linelen( dest );
  790.          if (action==MOVE || action==COPY || action==KOPY) {
  791.             if (lend < rcol || mode.inflate_tabs) {
  792.                add = prepare_block( &d_w, dest, lend, rcol );
  793.                if (!source_first) {
  794.                   source += add;
  795.                   block_start += add;
  796.                   block_end   += add;
  797.                }
  798.             }
  799.             dest += rcol;
  800.          }
  801.       }
  802.  
  803.       diff = ptoul( block_end ) - ptoul( block_start );
  804.       dest_add = source_sub = diff;
  805.       if (action != DELETE) {
  806.          p = addltop( diff, dest );
  807.          number = ptoul( g_status.end_mem ) - ptoul( dest );
  808.          hw_move( p, dest, number );
  809.          g_status.end_mem = addltop( diff, g_status.end_mem);
  810.       }
  811.       if (action != DELETE && !source_first)
  812.          source = addltop( diff, source );
  813.       if (action == COPY || action == KOPY || action == MOVE)
  814.          hw_move( dest, source, diff );
  815.       if (action == DELETE || action == MOVE) {
  816.          p = addltop( diff, source );
  817.          number = ptoul( g_status.end_mem ) - ptoul( p );
  818.          hw_move( source, p, number );
  819.          g_status.end_mem = addltop( -diff, g_status.end_mem);
  820.       }
  821.       if (action == DELETE)
  822.          dest_add = 0;
  823.       else if (action == COPY || action == KOPY)
  824.          source_sub = 0;
  825.       diff =  block_type == LINE ? (er+1l) - br : er - br;
  826.       if (action == COPY || action == KOPY || action == MOVE)
  827.          dest_file->length += diff;
  828.       if (action == DELETE || action == MOVE)
  829.          source_file->length -= diff;
  830.       if (action == DELETE && source_window->rline >= br) {
  831.          source_window->rline -= diff;
  832.          if (source_window->rline < br)
  833.             source_window->rline = br;
  834.       }
  835.       /*
  836.        * the block action is now complete.  restore all the start_text and
  837.        * end_text pointers for all open files.
  838.        */
  839.       if (action == MOVE || action == DELETE)
  840.          restore_start_end( dest_file, source_file, dest_add, -source_sub,
  841.                             source_first );
  842.       else
  843.          restore_start_end( dest_file, source_file, dest_add, source_sub,
  844.                             source_first );
  845.       /*
  846.        * restore all cursors in all windows
  847.        */
  848.       restore_cursors( dest_file, source_file );
  849.  
  850.    /*
  851.     *  -------  main loop for BOX blocks  -------------
  852.     */
  853.    } else {
  854.       padded_file = FALSE;
  855.       dup_window_info( &s_w, source_window );
  856.       dup_window_info( &d_w, window );
  857.       s_w.rline = br;
  858.  
  859.       /*
  860.        * special case for block actions.  since block actions always
  861.        *   move forward thru the file, overlapping text in an OVERLAY
  862.        *   action don't do right.  make the operation start at the end
  863.        *   of the block and work backwards.
  864.        */
  865.       if ((action == OVERLAY || action == SWAP) && same &&  rline > br &&
  866.                                                                rline <= er) {
  867.  
  868.          /*
  869.           * see if we need to add padd lines at eof.
  870.           */
  871.          dest_add = rline - br;
  872.          if (dest_add + er > window->file_info->length) {
  873.             dest_add = dest_add - (window->file_info->length - er);
  874.             for (; dest_add > 0; dest_add--) {
  875.                p = addltop( -1, dest_file->end_text );
  876.                pad_dest_line( window, dest_file, p );
  877.             }
  878.             padded_file = TRUE;
  879.             dup_window_info( &s_w, source_window );
  880.             dup_window_info( &d_w, window );
  881.          }
  882.  
  883.          /*
  884.           * move source and dest pointers to the end of the OVERLAY
  885.           */
  886.          for (li=er-br; li > 0; li--) {
  887.             load_undo_buffer( dest );
  888.             dest = find_next( dest );
  889.             ++d_w.rline;
  890.             source = find_next( source );
  891.             ++s_w.rline;
  892.          }
  893.  
  894.          /*
  895.           * work backwards so the overlapped OVERLAY block don't use
  896.           * overlayed text to fill the block.  same for SWAPPing blocks.
  897.           */
  898.          source = cpb( source );
  899.          dest   = cpb( dest );
  900.          for (li=er; li >= br  &&  !g_status.control_break; li--, s_w.rline--,
  901.                                                             d_w.rline--) {
  902.             lens = linelen( source );
  903.             lend = linelen( dest );
  904.             if (lens != 0 || lend != 0) {
  905.                d_w.cursor = dest;
  906.                load_box_buff( block_buff, cpf( source ), bc, ec, ' ' );
  907.                if (action == SWAP)
  908.                   load_box_buff( swap_buff, cpf( dest ), rcol, rcol+block_len, ' ' );
  909.                if (lend < rcol  ||  mode.inflate_tabs)
  910.                   prepare_block( &d_w, cpf( dest ), lend, rcol );
  911.                copy_buff_2file( &d_w, block_buff, cpf( dest ), rcol,
  912.                                        block_len, action );
  913.                if (action == SWAP) {
  914.                   add = 0;
  915.                   if (lens < bc || mode.inflate_tabs)
  916.                      add = prepare_block( &s_w, source, lens, bc );
  917.                   add += copy_buff_2file( &s_w, swap_buff, source, bc,
  918.                                    block_len, action );
  919.                   if (add < 0)
  920.                      dest = cpf( cpb( dest ) + add );
  921.                   else
  922.                      dest += add;
  923.                }
  924.             }
  925.             source = find_prev( source );
  926.             dest = find_prev( dest );
  927.          }
  928.       } else {
  929.          for (li=br; li<=er && !g_status.control_break; li++, s_w.rline++,
  930.                                                         d_w.rline++) {
  931.             lens = linelen( source );
  932.             lend = linelen( dest );
  933.  
  934.             switch (action) {
  935.                case FILL    :
  936.                case NUMBER  :
  937.                case DELETE  :
  938.                case MOVE    :
  939.                   load_undo_buffer( source );
  940.                   break;
  941.                case COPY    :
  942.                case KOPY    :
  943.                case OVERLAY :
  944.                   load_undo_buffer( dest );
  945.                   break;
  946.             }
  947.  
  948.             /*
  949.              * with FILL and NUMBER operations, we're just adding chars
  950.              *   to the file at the source location.  we don't have to
  951.              *   worry about bookkeeping.
  952.              */
  953.             if (action == FILL || action == NUMBER) {
  954.                s_w.cursor = source;
  955.                if (lens < rcol || mode.inflate_tabs)
  956.                   prepare_block( &s_w, source, lens, rcol );
  957.                if (action == NUMBER) {
  958.                  number_block_buff( block_buff, block_len, block_num, block_just );
  959.                  block_num += block_inc;
  960.                }
  961.                copy_buff_2file( &s_w, block_buff, source, rcol,
  962.                                    block_len, action );
  963.  
  964.             /*
  965.              * if we are doing a BOX action and both the source and
  966.              * destination are 0 then we have nothing to do.
  967.              */
  968.             } else if (lens != 0 || lend != 0) {
  969.  
  970.                /*
  971.                 * do actions that may require adding to file
  972.                 */
  973.                if (action == MOVE     ||  action == COPY || action == KOPY ||
  974.                    action == OVERLAY  ||  action == SWAP) {
  975.                   d_w.cursor = dest;
  976.                   xbc = bc;
  977.                   xec = ec;
  978.                   if (action != OVERLAY  &&  action != SWAP  &&  same) {
  979.                      if (rcol < bc && rline > br && rline <=er)
  980.                         if (li >= rline) {
  981.                            xbc = bc + block_len;
  982.                            xec = ec + block_len;
  983.                         }
  984.                   }
  985.                   load_box_buff( block_buff, source, xbc, xec, ' ' );
  986.                   if (action == SWAP)
  987.                      load_box_buff( swap_buff, dest, rcol, rcol+block_len, ' ' );
  988.                   add = 0;
  989.                   if (lend < rcol || mode.inflate_tabs)
  990.                      add = prepare_block( &d_w, dest, lend, rcol );
  991.                   add += copy_buff_2file( &d_w, block_buff, dest, rcol,
  992.                                    block_len, action );
  993.                   if (!source_first) {
  994.                      if (add < 0)
  995.                         source = cpf( cpb( source ) + add );
  996.                      else
  997.                         source += add;
  998.                   }
  999.                   if (action == SWAP) {
  1000.                      add = 0;
  1001.                      if (lens < xbc || mode.inflate_tabs)
  1002.                         add = prepare_block( &s_w, source, lens, xbc );
  1003.                      add += copy_buff_2file( &s_w, swap_buff, source, xbc,
  1004.                                       block_len, action );
  1005.                      if (source_first && same &&
  1006.                                ptoul( source ) != ptoul( dest )) {
  1007.                         if (add < 0)
  1008.                            dest = cpf( cpb( dest ) + add );
  1009.                         else
  1010.                            dest += add;
  1011.                      }
  1012.                   }
  1013.                }
  1014.  
  1015.                /*
  1016.                 * do actions that may require deleting from file
  1017.                 */
  1018.                if (action == MOVE || action == DELETE) {
  1019.                   s_w.cursor = source;
  1020.                   lens = find_end( source );
  1021.                   if (lens >= (bc + 1)) {
  1022.                      add = block_len;
  1023.                      xbc = bc;
  1024.                      if (lens <= (ec + 1))
  1025.                         add = lens - bc;
  1026.                      if (same && action == MOVE) {
  1027.                         if (rcol < bc && rline >= br && rline <=er)
  1028.                            if (li >= rline) {
  1029.                               xbc = bc + block_len;
  1030.                               if (lens <= (ec + block_len + 1))
  1031.                                  add = lens - xbc;
  1032.                            }
  1033.                      }
  1034.                      add = delete_box_block( &s_w, source, xbc, add,
  1035.                                                           prompt_line );
  1036.                      if (action == MOVE && source_first) {
  1037.                         if (!same || s_w.rline != d_w.rline) {
  1038.                            dest = addltop( -add, dest );
  1039.                            dest = cpf( dest );
  1040.                         }
  1041.                      }
  1042.                   }
  1043.                }
  1044.             }
  1045.  
  1046.             /*
  1047.              * if we are doing any BOX action we need to move the source pointer
  1048.              * to the next line.
  1049.              */
  1050.             source = find_next( source );
  1051.  
  1052.             /*
  1053.              * if we are doing any action other than DELETE, we need to move
  1054.              * the destination to the next line in marked block.
  1055.              * In BOX mode, we may need to pad the end of the file
  1056.              * with a blank line before we process the next line.
  1057.              */
  1058.             if (action != DELETE && action != FILL && action != NUMBER) {
  1059.                p = find_next( dest );
  1060.                if (p != NULL && *p != CONTROL_Z)
  1061.                   dest = p;
  1062.                else {
  1063.                   padded_file = TRUE;
  1064.                   p = addltop( -1, dest_file->end_text );
  1065.                   pad_dest_line( window, dest_file, p );
  1066.                   dest = find_next( dest );
  1067.                   if (!source_first)
  1068.                      ++source;
  1069.                }
  1070.             }
  1071.          }
  1072.       }
  1073.       if (padded_file) {
  1074.          w = g_status.window_list;
  1075.          while (w != NULL) {
  1076.             if (w->file_info == dest_file && w->visible )
  1077.                show_size( w );
  1078.             w = w->next;
  1079.          }
  1080.       }
  1081.    }
  1082.  
  1083.    dest_file->modified = TRUE;
  1084.    dest_file->dirty = GLOBAL;
  1085.    if (action == MOVE || action == DELETE || action == FILL || action==NUMBER) {
  1086.       source_file->modified = TRUE;
  1087.       source_file->dirty = GLOBAL;
  1088.    }
  1089.  
  1090.    /*
  1091.     * unless we are doing a KOPY, FILL, NUMBER, or OVERLAY we need to unmark
  1092.     * the block.  if we just did a KOPY, the beginning and ending may have
  1093.     * changed.  so, we must readjust beginning and ending rows.
  1094.     */
  1095.    if (action == KOPY) {
  1096.       if (same && !source_first && block_type == LINE) {
  1097.          number = (er+1) - br;
  1098.          source_file->block_br += number;
  1099.          source_file->block_er += number;
  1100.       } else if (same && !source_first && window->rline == br &&
  1101.                  block_type == BOX) {
  1102.          add = (ec+1) - bc;
  1103.          source_file->block_bc += add;
  1104.          source_file->block_ec += add;
  1105.       }
  1106.    } else if (action != FILL && action != OVERLAY && action != NUMBER)
  1107.       unmark_block( window );
  1108.    show_avail_mem( );
  1109.    g_status.copied = FALSE;
  1110.    return( OK );
  1111. }
  1112.  
  1113.  
  1114. /*
  1115.  * Name:    load_box_buff
  1116.  * Purpose: copy the contents of a BOX to a block buffer.
  1117.  * Date:    June 5, 1991
  1118.  * Passed:  block_buff: local buffer for block moves
  1119.  *          source: source line in file
  1120.  *          bc:     beginning column of BOX. used only in BOX operations.
  1121.  *          ec:     ending column of BOX. used only in BOX operations.
  1122.  *          filler: character to fill boxes that end past eol
  1123.  * Notes:   For BOX blocks, there are several things to take care of:
  1124.  *            1) The BOX begins and ends within a line - just copy the blocked
  1125.  *            characters to the block buff.  2) the BOX begins within a line
  1126.  *            but ends past the eol - copy all the characters within the line
  1127.  *            to the block buff then fill with padding.  3) the BOX begins and
  1128.  *            ends past eol - fill entire block buff with padding (filler).
  1129.  *          the fill character varies with the block operation.  for sorting
  1130.  *            a box block, the fill character is '\0'.  for adding text to
  1131.  *            the file, the fill character is a space.
  1132.  */
  1133. void load_box_buff( char *block_buff, text_ptr source, int bc, int ec,
  1134.                     char filler )
  1135. {
  1136. int len, pad, avlen;
  1137. register int i;
  1138. register char *bb;
  1139.  
  1140.    bb = block_buff;
  1141.    source = detab_a_line( source );
  1142.    len = linelen( source );
  1143.    /*
  1144.     * block start may be past eol
  1145.     */
  1146.    if (len < ec + 1) {
  1147.       /*
  1148.        * does block start past eol? - fill with pad
  1149.        */
  1150.       if (len < bc) {
  1151.          pad = (ec + 1) - bc;
  1152.          for (i=pad; i>0; i--)
  1153.             *bb++ = filler;
  1154.       } else {
  1155.          /*
  1156.           * block ends past eol - fill with pad
  1157.           */
  1158.          pad = (ec + 1) - len;
  1159.          avlen = len - bc;
  1160.          source = source + bc;
  1161.          for (i=avlen; i>0; i--)
  1162.             *bb++ = *source++;
  1163.          for (i=pad; i>0; i--)
  1164.             *bb++ = filler;
  1165.       }
  1166.    } else {
  1167.       /*
  1168.        * block is within line - copy block to buffer
  1169.        */
  1170.       avlen = (ec + 1) - bc;
  1171.       source = source + bc;
  1172.       for (i=avlen; i>0; i--)
  1173.          *bb++ = *source++;
  1174.    }
  1175.    *bb++ = CONTROL_Z;
  1176.    *bb = '\0';
  1177. }
  1178.  
  1179.  
  1180. /*
  1181.  * Name:    copy_buff_2file
  1182.  * Purpose: copy the contents of block buffer to destination file
  1183.  * Date:    June 5, 1991
  1184.  * Passed:  window:     pointer to current window
  1185.  *          block_buff: local buffer for moves
  1186.  *          dest:       pointer to destination line in destination file
  1187.  *          rcol:       if in BOX mode, destination column in destination file
  1188.  *          block_len:  if in BOX mode, width of block to copy
  1189.  *          action:     type of block action
  1190.  * Notes:   In BOX mode, the destination line has already been prepared.
  1191.  *          Just copy the BOX buffer to the destination line.
  1192.  */
  1193. int  copy_buff_2file( WINDOW *window, char *block_buff, text_ptr dest,
  1194.                       int rcol, int block_len, int action )
  1195. {
  1196. register char *s;
  1197. char *d;
  1198. int len;
  1199. int orig_len;
  1200.  
  1201.    orig_len = linelen( dest );
  1202.    copy_line( dest, window->bottom_line );
  1203.    if (mode.inflate_tabs)
  1204.       detab_linebuff( );
  1205.  
  1206.    s = g_status.line_buff + rcol;
  1207.  
  1208.    /*
  1209.     * s is pointing to location to perform BOX operation.  If we do a
  1210.     * FILL or OVERLAY, we do not necessarily add any extra space.  If the
  1211.     * line does not extend all the thru the BOX then we add.
  1212.     * we always add space when we COPY, KOPY, or MOVE
  1213.     */
  1214.    if (action == FILL || action == OVERLAY || action == NUMBER || action == SWAP) {
  1215.       len = linelen( s );
  1216.       if (len < block_len) {
  1217.          d = s + block_len - len;
  1218.          len = block_len + 1 + linelen( g_status.line_buff ) - rcol;
  1219.          memmove( d, s, len );
  1220.       }
  1221.    } else {
  1222.       d = s + block_len;
  1223.       len = block_len + 1 + linelen( g_status.line_buff ) - rcol;
  1224.       memmove( d, s, len );
  1225.    }
  1226.  
  1227.    memmove( s, block_buff, block_len );
  1228.    if (mode.inflate_tabs)
  1229.       entab_linebuff( );
  1230.    un_copy_line( dest, window, TRUE );
  1231.    return( linelen( dest ) - orig_len );
  1232. }
  1233.  
  1234.  
  1235. /*
  1236.  * Name:    block_fill
  1237.  * Purpose: fill the block buffer with character
  1238.  * Date:    June 5, 1991
  1239.  * Passed:  block_buff: local buffer for moves
  1240.  *          fill_char:  fill character
  1241.  *          block_len:  number of columns in block
  1242.  * Notes:   Fill block_buffer for block_len characters using fill_char.  This
  1243.  *          function is used only for BOX blocks.
  1244.  */
  1245. void block_fill( char *block_buff, int fill_char, int block_len )
  1246. {
  1247.    memset( block_buff, fill_char, block_len );
  1248.    *(block_buff+block_len) = CONTROL_Z;
  1249. }
  1250.  
  1251.  
  1252. /*
  1253.  * Name:    number_block_buff
  1254.  * Purpose: put a number into the block buffer
  1255.  * Date:    June 5, 1991
  1256.  * Passed:  block_buff: local buffer for moves
  1257.  *          block_len:  number of columns in block
  1258.  *          block_num:  long number to fill block
  1259.  *          just:       LEFT or RIGHT justified?
  1260.  * Notes:   Fill block_buffer for block_len characters with number.
  1261.  *          This function is used only for BOX blocks.
  1262.  */
  1263. void number_block_buff( char *block_buff, int block_len, long block_num,
  1264.                         int just )
  1265. {
  1266. int len;                /* length of number buffer */
  1267. int i;
  1268. char temp[MAX_COLS];    /* buffer for long number to ascii conversion  */
  1269.  
  1270.    block_fill( block_buff, ' ', block_len );
  1271.    len = strlen( ltoa( block_num, temp, 10 ) );
  1272.    if (just == RIGHT) {
  1273.       block_len--;
  1274.       len--;
  1275.       for (;block_len >= 0 && len >= 0; block_len--, len--)
  1276.          block_buff[block_len] = temp[len];
  1277.    } else {
  1278.       for (i=0; block_len > 0 && i < len; block_len--, i++)
  1279.          block_buff[i] = temp[i];
  1280.    }
  1281. }
  1282.  
  1283.  
  1284. /*
  1285.  * Name:    restore_start_end
  1286.  * Purpose: a file has been modified - must restore all start and end pointers
  1287.  * Date:    June 5, 1991
  1288.  * Passed:  dest_file:  pointer to destination file structure
  1289.  *          source_file:  pointer to source file structure
  1290.  *          dest_mod:  net modifications in the destination file
  1291.  *          source_mod:  net modifications in the source file
  1292.  *          source_first:  we must know which file is stored first in memory
  1293.  * Notes:   Go through the file list and adjust the start_text and end_text
  1294.  *          file pointers as needed.   There are several cases that must be
  1295.  *          be considered.  1) destination file and source file could be the
  1296.  *          same.  2) if the file pointer we're looking at is below both
  1297.  *          the source and destination, no action is needed.  3) the file
  1298.  *          we're looking at could be between the source and destination.
  1299.  *          4) the file we're looking at could be either source or destination.
  1300.  *          5) the file we're looking at could be past both source and dest.
  1301.  *          Use unsigned longs to compare pointers.
  1302.  */
  1303. void restore_start_end( file_infos *df, file_infos *source_file,
  1304.                         long dest_mod, long source_mod, int source_first )
  1305. {
  1306. int same;
  1307. long net_mod;
  1308. unsigned long sst;      /* source start_text - keep these around for if's */
  1309. unsigned long dst;      /* destination start_text */
  1310. unsigned long ost;      /* open_file start_text */
  1311. register file_infos *open_file;
  1312. register file_infos *dest_file;
  1313.  
  1314.    dest_file = df;
  1315.    net_mod = dest_mod + source_mod;
  1316.    sst = ptoul( source_file->start_text );
  1317.    dst = ptoul( dest_file->start_text );
  1318.    same =  sst == dst ? TRUE : FALSE;
  1319.    for (open_file=g_status.file_list; open_file != NULL;
  1320.              open_file=open_file->next) {
  1321.       sst = ptoul( source_file->start_text );
  1322.       dst = ptoul( dest_file->start_text );
  1323.       ost = ptoul( open_file->start_text );
  1324.       if (ost == sst) {
  1325.          if (same)
  1326.             source_file->end_text = addltop( net_mod, source_file->end_text);
  1327.          else if (source_first)
  1328.             source_file->end_text = addltop( source_mod,
  1329.                                              source_file->end_text);
  1330.          else {
  1331.             source_file->start_text = addltop( dest_mod,
  1332.                                              source_file->start_text);
  1333.             source_file->end_text = addltop( net_mod, source_file->end_text);
  1334.          }
  1335.       } else if (ost == dst) {
  1336.          if (source_first) {
  1337.             dest_file->start_text = addltop( source_mod,
  1338.                                              dest_file->start_text);
  1339.             dest_file->end_text = addltop( net_mod, dest_file->end_text);
  1340.          } else
  1341.             dest_file->end_text = addltop( dest_mod, dest_file->end_text);
  1342.       } else if (ost > sst) {
  1343.          if (ost < dst) {
  1344.             open_file->start_text = addltop( source_mod,
  1345.                                              open_file->start_text);
  1346.             open_file->end_text = addltop( source_mod, open_file->end_text);
  1347.          } else {
  1348.             open_file->start_text = addltop( net_mod, open_file->start_text);
  1349.             open_file->end_text = addltop( net_mod, open_file->end_text);
  1350.          }
  1351.       } else if (ost > dst) {
  1352.          if (ost < sst) {
  1353.             open_file->start_text = addltop( dest_mod, open_file->start_text);
  1354.             open_file->end_text = addltop( dest_mod, open_file->end_text);
  1355.          } else {
  1356.             open_file->start_text = addltop( net_mod, open_file->start_text);
  1357.             open_file->end_text = addltop( net_mod, open_file->end_text);
  1358.          }
  1359.       }
  1360.    }
  1361. }
  1362.  
  1363.  
  1364. /*
  1365.  * Name:    restore_cursors
  1366.  * Purpose: a file has been modified - must restore all cursor pointers
  1367.  * Date:    June 5, 1991
  1368.  * Passed:  dest_file:  target file for block actions
  1369.  *          source_file:  source file for block actions
  1370.  * Notes:   Go through the window list and adjust the cursor pointers
  1371.  *          as needed.   This could be done by using the changes made by
  1372.  *          the block actions, but it would be a real pain in the neck.
  1373.  *          I chose to use the brute force approach.
  1374.  */
  1375. void restore_cursors( file_infos *dest_file, file_infos *source_file )
  1376. {
  1377. register WINDOW *window;
  1378. register file_infos *file;
  1379. text_ptr p;
  1380. long beg_line, cur_line, test_line;
  1381. unsigned long df, sf, f;
  1382.  
  1383.    df = ptoul( (text_ptr)dest_file );
  1384.    sf = ptoul( (text_ptr)source_file );
  1385.    window = g_status.window_list;
  1386.    while (window != NULL) {
  1387.       file = window->file_info;
  1388.       f = ptoul( (text_ptr)file );
  1389.       beg_line = 1;
  1390.       cur_line = window->rline;
  1391.       if (cur_line > file->length) {
  1392.          file->end_text = cpb( file->end_text );
  1393.          p = find_prev( file->end_text-1 );
  1394.          window->cursor =  p != NULL ? p : file->start_text;
  1395.          window->rline = file->length;
  1396.          test_line = cur_line - file->length;
  1397.          if (test_line<(long)(window->cline-(window->top_line+window->ruler-1)))
  1398.             window->cline -= test_line;
  1399.       } else {
  1400.          file->start_text = cpf( file->start_text );
  1401.          for (p=file->start_text; p!=NULL && beg_line<cur_line; beg_line++)
  1402.             p = find_next( p );
  1403.          if (p != NULL )
  1404.             window->cursor = p;
  1405.          else {
  1406.             window->cursor = file->start_text;
  1407.             cur_line = file->length;
  1408.          }
  1409.          window->rline = cur_line;
  1410.       }
  1411.       if (window->rline <= 0l)
  1412.          window->rline = 1l;
  1413.       if (window->rline < (window->cline - (window->top_line+window->ruler-1)))
  1414.          window->cline = (int)window->rline + window->top_line+window->ruler-1;
  1415.       if (window->cline < window->top_line + window->ruler)
  1416.          window->cline = window->top_line + window->ruler;
  1417.       if ((f == df || f == sf) && window->visible )
  1418.          show_size( window );
  1419.       window = window->next;
  1420.    }
  1421. }
  1422.  
  1423.  
  1424. /*
  1425.  * Name:    delete_box_block
  1426.  * Purpose: delete the marked text
  1427.  * Date:    June 5, 1991
  1428.  * Passed:  s_w:    source window
  1429.  *          source: pointer to line with block to delete
  1430.  *          bc:     beginning column of block - BOX mode only
  1431.  *          add:    number of characters in block to delete
  1432.  *          prompt_line:  line to display error message if needed
  1433.  * Notes:   Used only for BOX blocks.  Delete the block.
  1434.  */
  1435. int  delete_box_block( WINDOW *s_w, text_ptr source, int bc, int add,
  1436.                        int prompt_line )
  1437. {
  1438. char *s;
  1439. int number;
  1440. int orig_len;
  1441.  
  1442.    copy_line( source, prompt_line );
  1443.    orig_len = linelen( g_status.line_buff );
  1444.    detab_linebuff( );
  1445.    number = linelen( g_status.line_buff ) - bc + 2;
  1446.    s = g_status.line_buff + bc + add;
  1447.    memmove( s - add, s, number );
  1448.    entab_linebuff( );
  1449.    add = orig_len - linelen( g_status.line_buff );
  1450.    un_copy_line( source, s_w, FALSE );
  1451.    return( add );
  1452. }
  1453.  
  1454.  
  1455. /*
  1456.  * Name:    check_block
  1457.  * Purpose: To check that the block is still valid.
  1458.  * Date:    June 5, 1991
  1459.  * Notes:   After some editing, the marked block may not be valid.  For example,
  1460.  *          deleting all the lines in a block in another window.  We don't
  1461.  *          need to keep up with the block text pointers while doing normal
  1462.  *          editing; however, we need to refresh them before doing block stuff.
  1463.  */
  1464. void check_block( void )
  1465. {
  1466. register file_infos *file;
  1467. WINDOW filler;
  1468.  
  1469.    file = g_status.marked_file;
  1470.    if (file == NULL || file->block_br > file->length)
  1471.       unmark_block( &filler );
  1472.    else {
  1473.       if (file->length < file->block_er)
  1474.          file->block_er = file->length;
  1475.       find_begblock( file );
  1476.       find_endblock( file );
  1477.    }
  1478. }
  1479.  
  1480.  
  1481. /*
  1482.  * Name:    find_begblock
  1483.  * Purpose: find the beginning line in file with marked block
  1484.  * Date:    June 5, 1991
  1485.  * Passed:  file: file containing marked block
  1486.  * Notes:   file->block_start contains starting line of marked block.
  1487.  */
  1488. void find_begblock( file_infos *file )
  1489. {
  1490. text_ptr next;    /* start from beginning of file and go to end */
  1491. long i;           /* line counter */
  1492.  
  1493.    next = cpf( file->start_text );
  1494.    for (i=1; i<file->block_br && next != NULL; i++)
  1495.       next = find_next( next );
  1496.    if (next != NULL)
  1497.       file->block_start = next;
  1498. }
  1499.  
  1500.  
  1501. /*
  1502.  * Name:    find_endblock
  1503.  * Purpose: find the ending line in file with marked block
  1504.  * Date:    June 5, 1991
  1505.  * Passed:  file: file containing marked block
  1506.  * Notes:   If in LINE mode, file->block_end is set to end of line of last
  1507.  *          line in block.  If in BOX mode, file->block_end is set to
  1508.  *          beginning of last line in marked block.  If the search for the
  1509.  *          ending line of the marked block goes past the eof, set the
  1510.  *          ending line of the block to the last line in the file.
  1511.  */
  1512. void find_endblock( file_infos *file )
  1513. {
  1514. text_ptr next;    /* start from beginning of file and go to end */
  1515. long i;           /* line counter */
  1516. int end_column;
  1517. register file_infos *fp;
  1518.  
  1519.    fp = file;
  1520.    next = cpf( fp->start_text );
  1521.    for (i=1; i<fp->block_er && next != NULL; i++)
  1522.       next = find_next( next );
  1523.    if (next != NULL) {
  1524.       end_column = linelen( next );
  1525.       if (next[end_column] == '\n')
  1526.          ++end_column;
  1527.  
  1528.       /*
  1529.        * if LINE block somewhere in the file, set block_end to first
  1530.        * line past end of marked block.
  1531.        */
  1532.       fp->block_end =  fp->block_type == LINE ? next + end_column : next;
  1533.    } else {
  1534.  
  1535.       /*
  1536.        * last line in marked block is NULL.  if LINE block, set end to
  1537.        * last character in the file.  if STREAM or BOX block, set end to
  1538.        * start of last line in file.  ending row, or er, is then set to
  1539.        * file length.
  1540.        */
  1541.       fp->end_text = cpb( fp->end_text );
  1542.       if (fp->block_type == LINE)
  1543.          fp->block_end = fp->end_text - 1;
  1544.       else {
  1545.          next = find_prev( fp->end_text - 1 );
  1546.          fp->block_end =  next != NULL ? next : fp->end_text - 1;
  1547.       }
  1548.       fp->block_er = fp->length;
  1549.    }
  1550. }
  1551.  
  1552.  
  1553. /*
  1554.  * Name:    block_write
  1555.  * Purpose: To write the currently marked block to a disk file.
  1556.  * Date:    June 5, 1991
  1557.  * Passed:  window:  pointer to current window
  1558.  * Notes:   If the file already exists, the user gets to choose whether
  1559.  *           to overwrite or append.
  1560.  */
  1561. int  block_write( WINDOW *window )
  1562. {
  1563. int prompt_line;
  1564. int rc;
  1565. char buff[MAX_COLS+2]; /* buffer for char and attribute  */
  1566. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1567. text_ptr block_start;   /* start of block in file */
  1568. text_ptr block_end;     /* end of block in file */
  1569. file_infos *file;
  1570. int block_type;
  1571. int fattr;
  1572.  
  1573.    /*
  1574.     * make sure block is marked OK
  1575.     */
  1576.    rc = ERROR;
  1577.    un_copy_line( window->cursor, window, TRUE );
  1578.    check_block( );
  1579.    if (g_status.marked == TRUE) {
  1580.       prompt_line = window->bottom_line;
  1581.       file        = g_status.marked_file;
  1582.       block_start = file->block_start;
  1583.       block_end   = file->block_end;
  1584.       block_type  = file->block_type;
  1585.  
  1586.       /*
  1587.        * find out which file to write to
  1588.        */
  1589.       save_screen_line( 0, prompt_line, line_buff );
  1590.       if (get_name( block6, prompt_line, g_status.rw_name,
  1591.                     g_display.message_color ) == OK) {
  1592.          /*
  1593.           * if the file exists, find out whether to overwrite or append
  1594.           */
  1595.          rc = get_fattr( g_status.rw_name, &fattr );
  1596.          if (rc == OK) {
  1597.             /*
  1598.              * file exists. overwrite or append?
  1599.              */
  1600.             set_prompt( block7, prompt_line );
  1601.             switch (get_oa( )) {
  1602.                case A_OVERWRITE :
  1603.                   change_mode( g_status.rw_name, prompt_line );
  1604.                   /*
  1605.                    * writing block to
  1606.                    */
  1607.                   combine_strings( buff, block8, g_status.rw_name, "'" );
  1608.                   s_output( buff, prompt_line, 0, g_display.message_color );
  1609.                   rc = hw_save( g_status.rw_name, block_start, block_end,
  1610.                                 block_type );
  1611.                   if (rc == ERROR)
  1612.                      /*
  1613.                       * could not write block
  1614.                       */
  1615.                      error( WARNING, prompt_line, block9 );
  1616.                   break;
  1617.                case A_APPEND :
  1618.                   /*
  1619.                    * appending block to
  1620.                    */
  1621.                   combine_strings( buff, block10, g_status.rw_name, "'" );
  1622.                   s_output( buff, prompt_line, 0, g_display.message_color );
  1623.                   rc = hw_append( g_status.rw_name, block_start, block_end,
  1624.                                   block_type );
  1625.                   if (rc == ERROR)
  1626.                      /*
  1627.                       * could not append block
  1628.                       */
  1629.                      error( WARNING, prompt_line, block11 );
  1630.                   break;
  1631.                case AbortCommand :
  1632.                   rc = ERROR;
  1633.                   break;
  1634.             }
  1635.          } else if (rc != ERROR) {
  1636.             /*
  1637.              * writing block to
  1638.              */
  1639.             combine_strings( buff, block12, g_status.rw_name, "'" );
  1640.             s_output( buff, prompt_line, 0, g_display.message_color );
  1641.             if (hw_save( g_status.rw_name, block_start, block_end,
  1642.                          block_type ) == ERROR) {
  1643.                /*
  1644.                 * could not write block
  1645.                 */
  1646.                error( WARNING, prompt_line, block9 );
  1647.                rc = ERROR;
  1648.             }
  1649.          }
  1650.       }
  1651.       restore_screen_line( 0, prompt_line, line_buff );
  1652.    } else
  1653.       rc = ERROR;
  1654.    return( rc );
  1655. }
  1656.  
  1657.  
  1658. /*
  1659.  * Name:    block_print
  1660.  * Purpose: Print an entire file or the currently marked block.
  1661.  * Date:    June 5, 1991
  1662.  * Passed:  window:  pointer to current window
  1663.  * Notes:   With the added Critical Error Handler routine, let's fflush
  1664.  *          the print buffer first.
  1665.  */
  1666. int  block_print( WINDOW *window )
  1667. {
  1668. char answer[MAX_COLS];          /* entire file or just marked block? */
  1669. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1670. int col, func;
  1671. int prompt_line;
  1672. text_ptr block_start;   /* start of block in file */
  1673. text_ptr block_end;     /* end of block in file */
  1674. file_infos *file;
  1675. int block_type;
  1676. char *p;
  1677. int len;
  1678. int bc, ec, last_c;
  1679. unsigned long lbegin, lend;
  1680. long l;
  1681. int color;
  1682. int rc;
  1683.  
  1684.    rc = OK;
  1685.    color = g_display.message_color;
  1686.    un_copy_line( window->cursor, window, TRUE );
  1687.    prompt_line = window->bottom_line;
  1688.    save_screen_line( 0, prompt_line, line_buff );
  1689.    /*
  1690.     * print entire file or just marked block?
  1691.     */
  1692.    strcpy( answer, block13 );
  1693.    col = strlen( answer );
  1694.    s_output( answer, prompt_line, 0, color );
  1695.    eol_clear( col, prompt_line, g_display.text_color );
  1696.    xygoto( col, prompt_line );
  1697.    func = col = 0;
  1698.    while (col != 'f' && col != 'F' && col != 'b' && col != 'B' &&
  1699.           func != AbortCommand) {
  1700.       col = getkey( );
  1701.       func = getfunc( col );
  1702.       if (col == ESC) {
  1703.          func = AbortCommand;
  1704.          rc = ERROR;
  1705.       }
  1706.    }
  1707.    fflush( stdprn );
  1708.    if (ceh.flag == ERROR) {
  1709.       func = AbortCommand;
  1710.       rc = ERROR;
  1711.    }
  1712.    if (func != AbortCommand) {
  1713.       file = window->file_info;
  1714.       if (col == 'f' || col == 'F') {
  1715.          block_start = file->start_text;
  1716.          block_end   = cpb( file->end_text ) - 1;
  1717.          block_type  = NOTMARKED;
  1718.          l           = file->length;
  1719.       } else if (col == 'b' || col == 'B') {
  1720.          check_block( );
  1721.          if (g_status.marked == TRUE) {
  1722.             file        = g_status.marked_file;
  1723.             block_start = file->block_start;
  1724.             block_end   = file->block_end;
  1725.             block_type  = file->block_type;
  1726.             l           = file->block_er + 1l - file->block_br;
  1727.          } else {
  1728.             col = AbortCommand;
  1729.             rc = ERROR;
  1730.          }
  1731.       }
  1732.       if (block_type == LINE) {
  1733.          block_end = cpb( block_end );
  1734.          block_end = find_prev( block_end );
  1735.       }
  1736.  
  1737.       if (col != AbortCommand) {
  1738.          eol_clear( 0, prompt_line, color );
  1739.          /*
  1740.           * printing line   of    press control-break to cancel.
  1741.           */
  1742.          s_output( block14, prompt_line, 0, color );
  1743.          ltoa( l, answer, 10 );
  1744.          s_output( answer, prompt_line, 25, color );
  1745.          xygoto( 14, prompt_line );
  1746.          block_start = cpf( block_start );
  1747.          if (block_type == BOX || block_type == STREAM) {
  1748.             bc = file->block_bc;
  1749.             ec = file->block_ec;
  1750.             last_c = ec + 1 - bc;
  1751.          }
  1752.          p = g_status.line_buff;
  1753.          lend = ptoul( block_end );
  1754.          for (l=1,col=OK; ptoul( block_start ) <= lend && col == OK &&
  1755.                                           !g_status.control_break; l++) {
  1756.             ltoa( l, answer, 10 );
  1757.             s_output( answer, prompt_line, 14, color );
  1758.             g_status.copied = FALSE;
  1759.             if (block_type == BOX) {
  1760.                load_box_buff( p, block_start, bc, ec, ' ' );
  1761.                *(p+last_c) = '\n';
  1762.                *(p+last_c+1) = CONTROL_Z;
  1763.             } else if (block_type == STREAM && l == 1) {
  1764.                len = linelen( block_start );
  1765.                lbegin = ptoul( block_start );
  1766.                block_start += bc < len ? bc : len;
  1767.                copy_line( block_start, prompt_line );
  1768.                if (lbegin == lend) {
  1769.                   if (len > ec) {
  1770.                      *(p+last_c) = '\n';
  1771.                      *(p+last_c+1) = CONTROL_Z;
  1772.                   }
  1773.                }
  1774.             } else if (block_type == STREAM && ptoul( block_start )==lend) {
  1775.                copy_line( block_start, prompt_line );
  1776.                if (linelen( block_start ) > (unsigned)ec) {
  1777.                   *(p+ec+1) = '\n';
  1778.                   *(p+ec+2) = CONTROL_Z;
  1779.                }
  1780.             } else
  1781.                copy_line( block_start, prompt_line );
  1782.             len = find_CONTROL_Z( p );
  1783.             if (fwrite( p, sizeof( char ), len, stdprn ) < (unsigned)len ||
  1784.                 ceh.flag == ERROR)
  1785.                col = ERROR;
  1786.             if (col != ERROR) {
  1787.                fputc( '\r', stdprn );
  1788.                if (ceh.flag == ERROR)
  1789.                   rc = col = ERROR;
  1790.             }
  1791.             block_start = find_next( block_start );
  1792.             if (block_start == NULL)
  1793.                block_start = block_end + 1;
  1794.          }
  1795.          g_status.copied = FALSE;
  1796.          if (ceh.flag != ERROR)
  1797.             fflush( stdprn );
  1798.       }
  1799.    }
  1800.    g_status.copied = FALSE;
  1801.    restore_screen_line( 0, prompt_line, line_buff );
  1802.    return( rc );
  1803. }
  1804.  
  1805.  
  1806. /*
  1807.  * Name:    get_block_fill_char
  1808.  * Purpose: get the character to fill marked block.
  1809.  * Date:    June 5, 1991
  1810.  * Passed:  window:  pointer to current window
  1811.  *          c: address of character to fill block
  1812.  */
  1813. int  get_block_fill_char( WINDOW *window, int *c )
  1814. {
  1815. char answer[MAX_COLS];
  1816. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1817. register int col;
  1818. int prompt_line;
  1819. int rc;
  1820.  
  1821.    rc = OK;
  1822.    prompt_line = window->bottom_line;
  1823.    save_screen_line( 0, prompt_line, line_buff );
  1824.    /*
  1825.     * enter character to file block (esc to exit)
  1826.     */
  1827.    strcpy( answer, block15 );
  1828.    s_output( answer, prompt_line, 0, g_display.message_color );
  1829.    col = strlen( answer );
  1830.    eol_clear( col, prompt_line, g_display.text_color );
  1831.    xygoto( col, prompt_line );
  1832.    col = getkey( );
  1833.    if (col >= 256)
  1834.       rc = ERROR;
  1835.    else
  1836.       *c = col;
  1837.    restore_screen_line( 0, prompt_line, line_buff );
  1838.    return( rc );
  1839. }
  1840.  
  1841.  
  1842. /*
  1843.  * Name:    get_block_numbers
  1844.  * Purpose: get the starting number and increment
  1845.  * Date:    June 5, 1991
  1846.  * Passed:  window:  pointer to current window
  1847.  *          block_num: address of number to start numbering
  1848.  *          block_inc: address of number to add to block_num
  1849.  *          just:      left or right justify numbers in block?
  1850.  */
  1851. int  get_block_numbers( WINDOW *window, long *block_num, long *block_inc,
  1852.                         int *just )
  1853. {
  1854. char answer[MAX_COLS];
  1855. int prompt_line;
  1856. register int rc;
  1857. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  1858. register int col;
  1859.  
  1860.    prompt_line = window->bottom_line;
  1861.  
  1862.    /*
  1863.     * don't assume anything on starting number - start w/ null string.
  1864.     */
  1865.    answer[0] = '\0';
  1866.    /*
  1867.     * enter starting number
  1868.     */
  1869.    rc = get_name( block16, prompt_line, answer, g_display.message_color );
  1870.    if (answer[0] == '\0')
  1871.       rc = ERROR;
  1872.    if (rc != ERROR) {
  1873.       *block_num = atol( answer );
  1874.  
  1875.       /*
  1876.        * assume increment is 1
  1877.        */
  1878.       answer[0] = '1';
  1879.       answer[1] = '\0';
  1880.       /*
  1881.        * enter increment
  1882.        */
  1883.       rc = get_name( block17, prompt_line, answer, g_display.message_color );
  1884.       if (answer[0] == '\0')
  1885.          rc = ERROR;
  1886.       if (rc != ERROR) {
  1887.          *block_inc = atol( answer );
  1888.  
  1889.          /*
  1890.           * now, get left or right justification.  save contents of screen
  1891.           *  in a buffer, then write contents of buffer back to screen when
  1892.           *  we get through w/ justification.
  1893.           */
  1894.          save_screen_line( 0, prompt_line, line_buff );
  1895.          /*
  1896.           * left or right justify (l/r)?
  1897.           */
  1898.          strcpy( answer, block18 );
  1899.          s_output( answer, prompt_line, 0, g_display.message_color );
  1900.          col = strlen( answer );
  1901.          eol_clear( col, prompt_line, g_display.text_color );
  1902.          xygoto( col, prompt_line );
  1903.          rc = get_lr( );
  1904.          if (rc != ERROR) {
  1905.             *just = rc;
  1906.             rc = OK;
  1907.          }
  1908.          restore_screen_line( 0, prompt_line, line_buff );
  1909.       }
  1910.    }
  1911.  
  1912.    /*
  1913.     * if everything is everything then return code = OK.
  1914.     */
  1915.    return( rc );
  1916. }
  1917.  
  1918.  
  1919. /*
  1920.  * Name:    block_trim_trailing
  1921.  * Purpose: Trim trailing space in a LINE block.
  1922.  * Date:    June 5, 1991
  1923.  * Passed:  window:  pointer to current window
  1924.  * Notes:   Use copy_line and un_copy_line to do the work.
  1925.  */
  1926. int  block_trim_trailing( WINDOW *window )
  1927. {
  1928. int prompt_line;
  1929. text_ptr p;                     /* pointer to block line */
  1930. file_infos *file;
  1931. WINDOW *sw, s_w;
  1932. long er;
  1933. int  trailing;               /* save trailing setting */
  1934.  
  1935.    /*
  1936.     * make sure block is marked OK and that this is a LINE block
  1937.     */
  1938.    prompt_line = window->bottom_line;
  1939.    un_copy_line( window->cursor, window, TRUE );
  1940.    check_block( );
  1941.    if (g_status.marked == TRUE) {
  1942.  
  1943.       trailing = mode.trailing;
  1944.       mode.trailing = TRUE;
  1945.       file = g_status.marked_file;
  1946.       if (file->block_type != LINE) {
  1947.          /*
  1948.           * can only trim trailing space in line blocks
  1949.           */
  1950.          error( WARNING, prompt_line, block21 );
  1951.          return( ERROR );
  1952.       }
  1953.  
  1954.       /*
  1955.        * set the command to word wrap so the un_copy_line function will
  1956.        * not display the lines while trimming.
  1957.        */
  1958.       g_status.command = WordWrap;
  1959.  
  1960.       /*
  1961.        * initialize everything
  1962.        */
  1963.       sw = g_status.window_list;
  1964.       for (; ptoul( sw->file_info ) != ptoul( file );)
  1965.          sw = sw->next;
  1966.       dup_window_info( &s_w, sw );
  1967.       p  = cpf( file->block_start );
  1968.       er = file->block_er;
  1969.       s_w.rline = file->block_br;
  1970.       for (; s_w.rline <= er  &&  !g_status.control_break; s_w.rline++) {
  1971.  
  1972.          /*
  1973.           * use the line buffer to trim space.
  1974.           */
  1975.          copy_line( p, prompt_line );
  1976.          un_copy_line( p, &s_w, TRUE );
  1977.          p = find_next( p );
  1978.       }
  1979.  
  1980.       /*
  1981.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  1982.        * not necessarily be on the last line of the block.
  1983.        */
  1984.       g_status.copied = FALSE;
  1985.       file->dirty = GLOBAL;
  1986.       mode.trailing = trailing;
  1987.    }
  1988.    return( OK );
  1989. }
  1990.  
  1991.  
  1992. /*
  1993.  * Name:    block_email_reply
  1994.  * Purpose: insert the standard replay character '>' at beginning of line
  1995.  * Date:    June 5, 1992
  1996.  * Passed:  window:  pointer to current window
  1997.  * Notes:   it is customary to prepend "> " to the initial text and
  1998.  *             ">" to replies to replies to etc...
  1999.  */
  2000. int  block_email_reply( WINDOW *window )
  2001. {
  2002. int prompt_line;
  2003. int add;
  2004. int len;
  2005. char *source;    /* source for block move to make room for c */
  2006. char *dest;      /* destination for block move */
  2007. text_ptr p;                     /* pointer to block line */
  2008. file_infos *file;
  2009. WINDOW *sw, s_w;
  2010. long er;
  2011.  
  2012.    /*
  2013.     * make sure block is marked OK and that this is a LINE block
  2014.     */
  2015.    prompt_line = window->bottom_line;
  2016.    un_copy_line( window->cursor, window, TRUE );
  2017.    check_block( );
  2018.    if (g_status.marked == TRUE) {
  2019.  
  2020.       /*
  2021.        * set the command to word wrap so the un_copy_line function will
  2022.        * not display the lines while doing block stuff.
  2023.        */
  2024.       g_status.command = WordWrap;
  2025.  
  2026.       file = g_status.marked_file;
  2027.       if (file->block_type != LINE) {
  2028.          /*
  2029.           * can only reply line blocks
  2030.           */
  2031.          error( WARNING, prompt_line, block25 );
  2032.          return( ERROR );
  2033.       }
  2034.  
  2035.       /*
  2036.        * find a window that points to the file with a marked block.
  2037.        */
  2038.       sw = g_status.window_list;
  2039.       for (; ptoul( sw->file_info ) != ptoul( file );)
  2040.          sw = sw->next;
  2041.  
  2042.       /*
  2043.        * use a local window structure to do the dirty work.  initialize
  2044.        *   the local window structure to the beginning of the marked
  2045.        *   block.
  2046.        */
  2047.       dup_window_info( &s_w, sw );
  2048.       p  = cpf( file->block_start );
  2049.       er = file->block_er;
  2050.       s_w.rline = file->block_br;
  2051.  
  2052.       /*
  2053.        * for each line in the marked block, prepend the reply character(s)
  2054.        */
  2055.       for (; s_w.rline <= er  &&  !g_status.control_break; s_w.rline++) {
  2056.  
  2057.          /*
  2058.           * put the line in the g_status.line_buff.  use add to count the
  2059.           *   number of characters to insert at the beginning of a line.
  2060.           *   the original reply uses "> ", while replies to replies use ">".
  2061.           */
  2062.          copy_line( p, prompt_line );
  2063.          if (*p == '>')
  2064.             add = 1;
  2065.          else
  2066.             add = 2;
  2067.  
  2068.          /*
  2069.           * see if the line has room to add the ">" character.  if there is
  2070.           *   room, move everything down to make room for the
  2071.           *   reply character(s).
  2072.           */
  2073.          len = linelen( g_status.line_buff );
  2074.          if (len + add < MAX_LINE_LENGTH) {
  2075.             source = g_status.line_buff;
  2076.             dest = source + add;
  2077.             memmove( dest, source, len+2 );
  2078.             *source = '>';
  2079.             if (add > 1)
  2080.               *(source+1) = ' ';
  2081.             un_copy_line( p, &s_w, TRUE );
  2082.          }
  2083.          p = find_next( p );
  2084.          g_status.copied = FALSE;
  2085.       }
  2086.  
  2087.       /*
  2088.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  2089.        * not necessarily be on the last line of the block.
  2090.        */
  2091.       g_status.copied = FALSE;
  2092.       file->dirty = GLOBAL;
  2093.    }
  2094.    return( OK );
  2095. }
  2096.  
  2097.  
  2098. /*
  2099.  * Name:    block_convert_case
  2100.  * Purpose: convert characters to lower case, upper case, strip hi bits,
  2101.  *          or e-mail functions
  2102.  * Date:    June 5, 1991
  2103.  * Passed:  window:  pointer to current window
  2104.  */
  2105. int  block_convert_case( WINDOW *window )
  2106. {
  2107. int len;
  2108. int block_type;
  2109. text_ptr begin;
  2110. text_ptr end;                   /* pointer to block line */
  2111. register file_infos *file;
  2112. WINDOW   *win;
  2113. unsigned long number;
  2114. unsigned long er;
  2115. unsigned int count;
  2116. int bc, ec;
  2117. int block_len;
  2118. void (*char_func)( text_ptr, unsigned int );
  2119.  
  2120.    /*
  2121.     * make sure block is marked OK
  2122.     */
  2123.    un_copy_line( window->cursor, window, TRUE );
  2124.    check_block( );
  2125.    if (g_status.marked == TRUE) {
  2126.  
  2127.       /*
  2128.        * set char_func() to the required block function in tdeasm.c
  2129.        */
  2130.       switch (g_status.command) {
  2131.          case BlockUpperCase  :
  2132.             char_func = upper_asm;
  2133.             break;
  2134.          case BlockLowerCase  :
  2135.             char_func = lower_asm;
  2136.             break;
  2137.          case BlockRot13      :
  2138.             char_func = rot13_asm;
  2139.             break;
  2140.          case BlockFixUUE     :
  2141.             char_func = fix_uue_asm;
  2142.             break;
  2143.          case BlockStripHiBit :
  2144.             char_func = strip_asm;
  2145.             break;
  2146.       }
  2147.  
  2148.       file  = g_status.marked_file;
  2149.       block_type = file->block_type;
  2150.       bc = file->block_bc;
  2151.       ec = file->block_ec;
  2152.  
  2153.       begin  = cpf( file->block_start );
  2154.       end    = cpf( file->block_end );
  2155.  
  2156.       /*
  2157.        * if this is a LINE or STREAM block, process characters in
  2158.        *   chunks of 0xf000.
  2159.        */
  2160.       if (block_type == LINE || block_type == STREAM) {
  2161.          if (block_type == STREAM) {
  2162.             len = linelen( begin );
  2163.             begin += len < bc ? len : bc;
  2164.             len = linelen( end );
  2165.             end += len < ec ? len : ec + 1;
  2166.          }
  2167.          number = ptoul( end ) - ptoul( begin );
  2168.          count = 0xf000;
  2169.          begin = nptos( begin );
  2170.          while (number > count) {
  2171.             (*char_func)( begin, count );
  2172.             number -= count;
  2173.             begin = nptos( begin + count );
  2174.          }
  2175.          /*
  2176.           * now less than 0xf000 is left, so finish off the conversion
  2177.           */
  2178.          (*char_func)( begin, (unsigned)number );
  2179.  
  2180.       /*
  2181.        * For BOX blocks, process characters by lines
  2182.        */
  2183.       } else {
  2184.          begin = cpf( begin );
  2185.          er = file->block_er;
  2186.          block_len = ec + 1 - bc;
  2187.          for (number=file->block_br; number <= er; number++) {
  2188.             len = linelen( begin );
  2189.             if (len > bc) {
  2190.                count =  len >= ec ? block_len : len - bc;
  2191.                (*char_func)( begin+bc, count );
  2192.             }
  2193.             begin = find_next( begin );
  2194.          }
  2195.       }
  2196.  
  2197.       /*
  2198.        * when we strip the hi bit from WordStar files, any soft line feeds
  2199.        *   will be converted into hard line feeds.  we need to get a new
  2200.        *   count of the number of lines, as we may need to adjust cursors
  2201.        *   and file size if lines were added to the file.
  2202.        */
  2203.       if (g_status.command == BlockStripHiBit) {
  2204.          er = 0;
  2205.          begin = cpf( file->start_text );
  2206.          while ((begin = find_next( begin )) != NULL)
  2207.             er++;
  2208.          if (file->length != (long)er) {
  2209.             file->length = (long)er;
  2210.             for (win=g_status.window_list; win != NULL; win=win->next) {
  2211.                if (win->file_info == file) {
  2212.                   er = 1;
  2213.                   for (win->cursor=file->start_text; (long)er<win->rline; er++)
  2214.                      win->cursor = find_next( win->cursor );
  2215.                   if (win->visible)
  2216.                      show_size( win );
  2217.                }
  2218.             }
  2219.          }
  2220.       }
  2221.  
  2222.       /*
  2223.        * IMPORTANT:  we need to reset the copied flag because the cursor may
  2224.        * not necessarily be on the last line of the block.
  2225.        */
  2226.       g_status.copied = FALSE;
  2227.       file->dirty = GLOBAL;
  2228.       file->modified = TRUE;
  2229.    }
  2230.    return( OK );
  2231. }
  2232.  
  2233.  
  2234. /*
  2235.  * Name:    sort_box_block
  2236.  * Purpose: sort lines according to text in marked BOX block
  2237.  * Date:    June 5, 1992
  2238.  * Passed:  window:  pointer to current window
  2239.  * Notes:   insertion sort the lines in the BOX buff according to stuff in
  2240.  *            a box block.
  2241.  */
  2242. int  sort_box_block( WINDOW *window )
  2243. {
  2244. int prompt_line;
  2245. int block_type;
  2246. unsigned int low;
  2247. unsigned int high;
  2248. register file_infos *file;
  2249. int  rc;
  2250. char lines[MAX_COLS];
  2251. char line_buff[(MAX_COLS+1)*2]; /* buffer for char and attribute  */
  2252.  
  2253.    /*
  2254.     * make sure block is marked OK
  2255.     */
  2256.    rc = OK;
  2257.    prompt_line = window->bottom_line;
  2258.    un_copy_line( window->cursor, window, TRUE );
  2259.    check_block( );
  2260.    if (g_status.marked == TRUE) {
  2261.       file  = g_status.marked_file;
  2262.       block_type = file->block_type;
  2263.       if (block_type == BOX) {
  2264.          /*
  2265.           * sort ascending or descending?
  2266.           */
  2267.          rc = get_sort_order( window );
  2268.          if (rc != ERROR) {
  2269.  
  2270.             /*
  2271.              * check pointers and begin and ending line numbers.
  2272.              */
  2273.             g_status.end_mem  = cpf( g_status.end_mem );
  2274.             file->block_start = cpf( file->block_start );
  2275.             sort.block_len = file->block_ec + 1 - file->block_bc;
  2276.             low  = 1;
  2277.             high = (unsigned int)(file->block_er - file->block_br + 1l);
  2278.  
  2279.             /*
  2280.              * save the prompt line and print out the master sort message.
  2281.              */
  2282.             save_screen_line( 0, prompt_line, line_buff );
  2283.             eol_clear( 0, prompt_line, g_display.text_color );
  2284.             /*
  2285.              * sorting line  of   press control-break to cancel
  2286.              */
  2287.             s_output( block22, prompt_line, 0, g_display.message_color );
  2288.             ultoa( high, lines, 10 );
  2289.             s_output( lines, prompt_line, 22, g_display.message_color );
  2290.  
  2291.             /*
  2292.              * simple insertion sort the box block.
  2293.              */
  2294.             sort.string1 = (char *)calloc( BUFF_SIZE+2, sizeof(char) );
  2295.             sort.pivot   = (char *)calloc( BUFF_SIZE+2, sizeof(char) );
  2296.             if (sort.string1 != NULL && sort.pivot != NULL)
  2297.                insertion_sort_block( low, high, prompt_line );
  2298.             free( sort.string1 );
  2299.             free( sort.pivot );
  2300.  
  2301.             /*
  2302.              * after sort finishes, restore prompt line and mark file as dirty.
  2303.              */
  2304.             restore_screen_line( 0, prompt_line, line_buff );
  2305.             file->dirty = GLOBAL;
  2306.             file->modified = TRUE;
  2307.             restore_cursors( file, file );
  2308.          }
  2309.       } else {
  2310.          /*
  2311.           * can only sort box blocks
  2312.           */
  2313.          error( WARNING, prompt_line, block23 );
  2314.          rc = ERROR;
  2315.       }
  2316.    } else {
  2317.       /*
  2318.        * box not marked
  2319.        */
  2320.       error( WARNING, prompt_line, block24 );
  2321.       rc = ERROR;
  2322.    }
  2323.    return( rc );
  2324. }
  2325.  
  2326.  
  2327. /*
  2328.  * Name:    insertion_sort_block
  2329.  * Purpose: sort lines according to text in marked BOX block
  2330.  * Date:    June 5, 1992
  2331.  * Passed:  low:          starting line in box block
  2332.  *          high:         ending line in a box block
  2333.  *          prompt_line:  line to display messages
  2334.  * Notes:   Insertion sort the lines in the BOX buff according to stuff in
  2335.  *            a box block.
  2336.  *          use whatever memory that is between the end of the file buffer
  2337.  *            and absolute end of memory to switch lines.  that way, we don't
  2338.  *            have to allocate space on the stack or in the heap for
  2339.  *            switching long lines.
  2340.  *          TDE explicity supports lines as long as 1040 chars.  by using the
  2341.  *            space between end_mem and max_mem when we swap lines, we can
  2342.  *            implicitly handle fairly long lines.
  2343.  */
  2344. void insertion_sort_block(unsigned int low, unsigned int high, int prompt_line)
  2345. {
  2346. unsigned int change;            /* relative line number for insertion sort */
  2347. unsigned int down;              /* relative line number for insertion sort */
  2348. register unsigned int pivot;    /* relative line number of pivot in block */
  2349. text_ptr pivot_text;            /* pointer to actual text in block */
  2350. text_ptr next_pivot;            /* pointer to next line after pivot line */
  2351. text_ptr down_text;             /* pointer used to compare text */
  2352. char lines[MAX_COLS];
  2353.  
  2354.    /*
  2355.     * make sure we have more than 1 line to sort.
  2356.     */
  2357.    if (low < high) {
  2358.  
  2359.       /*
  2360.        * reset the control-break flag then initialize the sort structure.
  2361.        */
  2362.       sort.bc  = g_status.marked_file->block_bc;
  2363.       sort.ec  = g_status.marked_file->block_ec;
  2364.       sort.compare = mode.search_case == IGNORE ? my_memicmp : my_memcmp;
  2365.       sort.free_mem = ptoul( g_status.max_mem ) - ptoul( g_status.end_mem );
  2366.  
  2367.       /*
  2368.        * setup pointer to pivot and next pivot.  when we swap lines, we
  2369.        *   we will likely loose track of the next line, because swapping
  2370.        *   variable length lines will leave the pivot_text pointer in an
  2371.        *   unstable position.  so, we need to save next_line.
  2372.        */
  2373.       pivot_text = set_sort_begin( low + 1 );
  2374.       next_pivot = find_next( pivot_text );
  2375.       xygoto( 13, prompt_line );
  2376.       for (pivot=low+1; pivot <= high  &&  !g_status.control_break; pivot++) {
  2377.  
  2378.          /*
  2379.           * print out current line
  2380.           */
  2381.          ultoa( pivot, lines, 10 );
  2382.          s_output( lines, prompt_line, 13, g_display.message_color );
  2383.  
  2384.          /*
  2385.           * set up the pivot array.  the pivot contains the key we want
  2386.           *   to sort.
  2387.           */
  2388.          load_pivot( pivot_text );
  2389.          down_text = find_prev( cpb( pivot_text ) );
  2390.          change = pivot;
  2391.          for (down=pivot-1; down >= low; down--) {
  2392.             /*
  2393.              * lets keep comparing the keys until we find the hole for
  2394.              *   pivot.
  2395.              */
  2396.             if (compare_pivot( down_text ) > 0) {
  2397.                /*
  2398.                 * if we are not at the first line of the block, find
  2399.                 *   the previous line.  set change to down so we will know
  2400.                 *   when we need to swap lines.
  2401.                 */
  2402.                change = down;
  2403.                if (down > low)
  2404.                   down_text = find_prev( down_text );
  2405.             } else {
  2406.                /*
  2407.                 * depending on the sort order, key is either bigger or
  2408.                 *   smaller than the rest of the lines in the top of the
  2409.                 *   box block.  since we may have just done a find_prev
  2410.                 *   in the if above, we need to move down_text back to
  2411.                 *   the line that needs to be swapped.
  2412.                 */
  2413.                down_text = find_next( cpf( down_text ) );
  2414.                break;
  2415.             }
  2416.          }
  2417.  
  2418.          if (change != pivot)
  2419.             slide_down( down_text, pivot_text );
  2420.          next_pivot = find_next( pivot_text = next_pivot );
  2421.       }
  2422.    }
  2423. }
  2424.  
  2425.  
  2426. /*
  2427.  * Name:    set_sort_begin
  2428.  * Purpose: set pointer to line in block
  2429.  * Date:    June 5, 1992
  2430.  * Passed:  line:  number of line in block
  2431.  */
  2432. text_ptr set_sort_begin( unsigned int line )
  2433. {
  2434. text_ptr text;
  2435. register unsigned int i;
  2436.  
  2437.    text = g_status.marked_file->block_start;
  2438.    for (i=1; i < line; i++)
  2439.       text = find_next( text );
  2440.    return( text );
  2441. }
  2442.  
  2443.  
  2444. /*
  2445.  * Name:    slide_down
  2446.  * Purpose: slide everything down then put the high line in low memory
  2447.  * Date:    June 5, 1992
  2448.  * Passed:  low_text:  pointer to line in lowest memory
  2449.  *          high_text: pointer to line in high memory
  2450.  * Notes:   copy high line to end of memory, then slide all the text from
  2451.  *          the low line to the space left by the high line.  to complete
  2452.  *          the move, copy the high line from end of memory to low memory.
  2453.  */
  2454. void slide_down( text_ptr low_text, text_ptr high_text )
  2455. {
  2456. unsigned long number;
  2457. register unsigned int len2;
  2458.  
  2459.    low_text = nptos( low_text );
  2460.    len2  = linelen( high_text = nptos( high_text ) ) + 1;
  2461.    if ((unsigned long)len2  > sort.free_mem)
  2462.       len2 = (unsigned)sort.free_mem - 1;
  2463.  
  2464.    number = ptoul( high_text ) - ptoul( low_text );
  2465.    _fmemcpy( g_status.end_mem, high_text, len2 );
  2466.    hw_move( low_text+len2, low_text, number );
  2467.    _fmemcpy( low_text, g_status.end_mem, len2 );
  2468. }
  2469.  
  2470.  
  2471. /*
  2472.  * Name:    load_pivot
  2473.  * Purpose: load pivot point for insertion sort
  2474.  * Date:    June 5, 1992
  2475.  * Passed:  text:  line that contains the pivot
  2476.  */
  2477. void load_pivot( text_ptr text )
  2478. {
  2479.    load_box_buff( sort.pivot, cpf( text ), sort.bc, sort.ec, '\0' );
  2480. }
  2481.  
  2482.  
  2483. /*
  2484.  * Name:    compare_pivot
  2485.  * Purpose: compare pivot string with text string
  2486.  * Date:    June 5, 1992
  2487.  * Passed:  text:  pointer to current line
  2488.  */
  2489. int  compare_pivot( text_ptr text )
  2490. {
  2491.    load_box_buff( sort.string1, cpf( text ), sort.bc, sort.ec, '\0' );
  2492.  
  2493.    if (sort.sort_order == ASCENDING)
  2494.       return( (*sort.compare)( sort.string1, sort.pivot, sort.block_len ) );
  2495.    else
  2496.       return( (*sort.compare)( sort.pivot, sort.string1, sort.block_len ) );
  2497. }
  2498.  
  2499.  
  2500. /*
  2501.  * Name:    my_memicmp
  2502.  * Purpose: compare strings using ignore case sort order
  2503.  * Date:    October 31, 1992
  2504.  * Passed:  s1:  pointer to string 1
  2505.  *          s2:  pointer to string 2
  2506.  *          len: number of characters to compare
  2507.  * Notes:   let's do our own memcmp, so we can sort languages that use
  2508.  *          extended characters as part of their alphabet.
  2509.  */
  2510. int  my_memicmp( unsigned char far *s1, unsigned char far *s2, unsigned len )
  2511. {
  2512.    if (len == 0)
  2513.       return( 0 );
  2514.  
  2515.    len--;
  2516.    for (; len > 0  &&  sort_order.ignore[*s1] == sort_order.ignore[*s2];
  2517.                                                            s1++, s2++, len--);
  2518.  
  2519.    return( (int)sort_order.ignore[*s1] - (int)sort_order.ignore[*s2] );
  2520. }
  2521.  
  2522.  
  2523. /*
  2524.  * Name:    my_memcmp
  2525.  * Purpose: compare strings using match case sort order
  2526.  * Date:    October 31, 1992
  2527.  * Passed:  s1:  pointer to string 1
  2528.  *          s2:  pointer to string 2
  2529.  *          len: number of characters to compare
  2530.  * Notes:   let's do our own memcmp, so we can sort languages that use
  2531.  *          extended characters as part of their alphabet.
  2532.  */
  2533. int  my_memcmp( unsigned char far *s1, unsigned char far *s2, unsigned len )
  2534. {
  2535.    if (len == 0)
  2536.       return( 0 );
  2537.  
  2538.    len--;
  2539.    for (; len > 0  &&  sort_order.match[*s1] == sort_order.match[*s2];
  2540.                                                            s1++, s2++, len--);
  2541.  
  2542.    return( sort_order.match[*s1] - sort_order.match[*s2] );
  2543. }
  2544.